<template>
  <div
    id="spreadsheet-container"
    :class="['c-spreadsheet', customConfig.isReport ? 'report' : 'noReport']"
  >
    <slot name="content"></slot>
    <div class="toolbar flex">
      <div class="upload" v-if="customConfig.showUpload">
        <input
          type="file"
          @change="loadExcelFile"
          ref="upload"
          accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel"
        />
      </div>
      <!-- <Button @click="initData" type="primary">初始化</Button> -->
      <Button
        v-if="customConfig.showExport"
        @click="exportExcel"
        type="primary"
        ghost
        >导出xlsx</Button
      >
      <Button
        v-if="customConfig.showExportPdf"
        @click="exportPdf"
        type="primary"
        ghost
        >导出pdf</Button
      >
      <!-- <Button v-if="customConfig.showLoadData"
                @click="setTemplateData" type="primary">绑定数据</Button> -->
      <Button v-if="customConfig.showSave" @click="saveData" type="primary"
        >保存</Button
      >
      <slot name="toolbar"></slot>
    </div>
    <slot size="large"></slot>
    <div class="spreadsheetWp">
      <div ref="sheetContainer" id="spreadsheetEl"></div>
      <Spin fix v-show="loading"></Spin>
    </div>
  </div>
</template>

<script>
// 引入依赖包
import Spreadsheet from "x-data-spreadsheet"; // https://hondrytravis.com/x-spreadsheet-doc/
import zhCN from "x-data-spreadsheet/src/locale/zh-cn";
import * as XLSX from "xlsx";
import * as Excel from "exceljs/dist/exceljs";
import * as tinycolor from "tinycolor2";
import { defaultConfig, defaultCustomConfig } from "./config";
import html2canvas from "html2canvas";
import { jsPDF } from "jspdf";

export default {
  name: "xspreadsheet",
  props: {
    config: {
      type: Object,
      default() {
        return defaultConfig;
      },
    },
    customConfig: {
      type: Object,
      default() {
        return defaultCustomConfig;
      },
    },
    templateData: {
      // 模板数据：包含样式的表格数据
      type: Array,
      default() {
        return [];
      },
    },
    bindData: {
      // 绑定数据：表格绑定的数据源
      type: Object,
      default() {
        return {};
      },
    },
    loading: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      xs: null,
      bindObj: {},
      _templateData: null,
      outImg: "",
    };
  },
  // computed: {
  //     _config() {
  //         return Object.assign(JSON.parse(JSON.stringify(customConfig)), this.customConfig)
  //     }
  // },
  watch: {
    templateData: {
      handler() {
        this._templateData = null;
        this.setTemplateData();
      },
      deep: true,
    },
    bindData: {
      handler(val) {
        console.log(val, "val");
        typeof val === "object" && this.setTemplateData();
      },
      deep: true,
    },
  },
  mounted() {
    this.init();
  },
  methods: {
    init() {
      let config = Object.assign(defaultConfig, this.config);
      if (this.customConfig.isReport) {
        config = {
          col: {
            ...(config.col || {}),
            indexWidth: 40,
          },
          view: {
            width: () => window.innerWidth + 25,
            height: () => window.innerHeight - 132,
          },
          ...config,
          // mode: 'read',
          showToolbar: false,
          showBottomBar: false,
          showContextmenu: false,
        };
      }
      Spreadsheet.locale("zh-cn", zhCN);
      this.xs = new Spreadsheet("#spreadsheetEl", config).change((cdata) => {});
      window.xs = this.xs;
      this.setTemplateData();
    },
    initData() {
      let tmpData = JSON.parse(
        JSON.stringify(this._templateData || this.templateData)
      );
      this.xs.loadData(tmpData);
    },
    saveData() {
      let tmpData = this.xs.getData();
      this.tools.copy(`export default ${JSON.stringify(tmpData)}`);
      this.$emit("saveData", tmpData);
    },
    setTemplateData(bindData = true) {
      let tmpData = JSON.parse(
        JSON.stringify(this._templateData || this.templateData)
      );
      this.xs.loadData(tmpData);
      if (this.customConfig.isReport) {
        bindData && this.setBindData(tmpData);
      }
    },
    // 通过 绑定的数据，重新组织数据，并加载到excel
    setBindData(tmpData) {
      tmpData = tmpData[0];

      if (!tmpData || !tmpData.rows || !tmpData.rows[0]) {
        return;
      }
      let reg = /<#([a-zA-Z_]+)#>\$\./;
      let rowItem, cellItem;
      let totalAddCount = 0;

      // 先根据数据添加空行
      Object.keys(tmpData.rows).forEach((key) => {
        let bindListKey = "";
        rowItem = tmpData.rows[key];

        rowItem.cells &&
          Object.keys(rowItem.cells).forEach((cellKey) => {
            cellItem = rowItem.cells[cellKey];

            // 绑定列表字段：<#listX#>$.code、<#listX#>$.name、<#listX#>$.count ...
            if (reg.test(cellItem.text)) {
              bindListKey = cellItem.text.match(reg)[1];
            }
          });
        if (bindListKey) {
          let curBind = this.bindData[bindListKey];
          let bindLen = curBind ? curBind.length : 0;

          if (curBind && bindLen) {
            let emptyRow = tmpData.rows[key * 1 + 1];
            if (!emptyRow || !emptyRow.cells) {
              this.xs.datas[0].rows.setHide(key * 1 + 1 + totalAddCount, true);
            }
            this.xs.datas[0].rows.insert(
              key * 1 + 1 + totalAddCount,
              bindLen - 1
            );
            totalAddCount += bindLen - 1;
          }
        }
      });
      // this.xs.reRender()
      // return

      // 绑定数据
      let newRows = JSON.parse(JSON.stringify(this.xs.getData()[0].rows));
      let rowsArr = Object.keys(newRows);

      rowsArr.forEach((key) => {
        let listItem = { fields: {} };
        rowItem = newRows[key];

        rowItem &&
          rowItem.cells &&
          Object.keys(rowItem.cells).forEach((cellKey) => {
            cellItem = rowItem.cells[cellKey];

            // 绑定列表字段：<#listX#>$.code、<#listX#>$.name、<#listX#>$.count ...
            if (reg.test(cellItem.text)) {
              listItem.key = cellItem.text.match(reg)[1];
              listItem.fields[cellKey] = cellItem.text.replace(
                new RegExp(`<#${listItem.key}#>\\$\\.`, "g"),
                `$.${listItem.key}_item.`
              );
            }
            // 绑定单个字段： $.code、$.name ...
            else if (/\$\./.test(cellItem.text)) {
              newRows[key * 1].cells[cellKey].text = this.calFormula(
                this.bindData,
                cellItem.text
              );
            }
          });
        if (listItem.key) {
          let curBind = this.bindData[listItem.key] || [{}];

          if (curBind && curBind.length) {
            // 插入行数据
            curBind.forEach((bindItem, bindIndx) => {
              let tmpCells = {};
              Object.keys(listItem.fields).forEach((fieldItem) => {
                tmpCells[fieldItem] = {
                  text: this.calFormula(
                    {
                      [`${listItem.key}_item`]: { ...bindItem },
                      ...this.bindData,
                    },
                    listItem.fields[fieldItem]
                  ),
                };
              });
              newRows[key * 1 + bindIndx] = { cells: tmpCells };
            });
          }
        }
      });
      let newData = {
        ...tmpData,
        rows: newRows,
      };
      this.setViewSize(newData);
      this.xs.loadData(newData);
    },
    // 根据内容，自动设置col和row的len
    setViewSize(tmpData) {
      let rowArr = Object.keys(tmpData.rows);
      // let colArr = Object.keys(tmpData.cols)
      // let maxCell = this.xs.datas[0].rows.maxCell()

      tmpData.rows.len = rowArr[rowArr.length - 2] * 1 + 2;
      // tmpData.cols.len = colArr[colArr.length - 2] * 1 + 2
    },
    // 向x-spreadsheet 带样式导入Excel
    loadExcelFile(e) {
      const wb = new Excel.Workbook();
      const reader = new FileReader();
      let that = this;

      reader.readAsArrayBuffer(e.target.files[0]);
      reader.onload = () => {
        const buffer = reader.result;
        wb.xlsx.load(buffer).then((workbook) => {
          let workbookData = [];
          workbook.eachSheet((sheet, sheetIndex) => {
            // 构造x-data-spreadsheet 的 sheet 数据源结构
            let sheetData = {
              name: sheet.name,
              styles: [],
              rows: {},
              merges: [],
            };
            // 收集合并单元格信息
            let mergeAddressData = [];
            for (let mergeRange in sheet._merges) {
              sheetData.merges.push(sheet._merges[mergeRange].shortRange);
              let mergeAddress = {};
              // 合并单元格起始地址
              mergeAddress.startAddress = sheet._merges[mergeRange].tl;
              // 合并单元格终止地址
              mergeAddress.endAddress = sheet._merges[mergeRange].br;
              // Y轴方向跨度
              mergeAddress.YRange =
                sheet._merges[mergeRange].model.bottom -
                sheet._merges[mergeRange].model.top;
              // X轴方向跨度
              mergeAddress.XRange =
                sheet._merges[mergeRange].model.right -
                sheet._merges[mergeRange].model.left;
              mergeAddressData.push(mergeAddress);
            }

            // 遍历行
            let maxRow = 0;
            sheet.eachRow((row, rowIndex) => {
              rowIndex = (rowIndex - 1).toString();
              sheetData.rows[rowIndex] = { cells: {} };
              maxRow = Math.max(maxRow, row._cells.length);
              //includeEmpty = false 不包含空白单元格
              row.eachCell({ includeEmpty: true }, function (cell, colNumber) {
                let cellText = "";
                colNumber = (colNumber - 1).toString();

                if (cell.value && cell.value.formula) {
                  // Excel 单元格有公式
                  cellText = `=${cell.value.formula}`;
                } else if (cell.value && cell.value.richText) {
                  // Excel 单元格是多行文本
                  for (let text in cell.value.richText) {
                    // 多行文本做累加
                    cellText += cell.value.richText[text].text;
                  }
                } else {
                  // Excel 单元格无公式
                  cellText = cell.value;
                }

                //解析单元格,包含样式
                //*********************单元格存在背景色******************************
                // 单元格存在背景色
                let backGroundColor = null;
                if (
                  cell.style.fill &&
                  cell.style.fill.fgColor &&
                  cell.style.fill.fgColor.argb
                ) {
                  // 8位字符颜色先转rgb再转16进制颜色
                  backGroundColor = ((val) => {
                    val = val.trim().toLowerCase(); //去掉前后空格
                    let color = {};
                    try {
                      let argb =
                        /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(
                          val
                        );
                      color.r = parseInt(argb[2], 16);
                      color.g = parseInt(argb[3], 16);
                      color.b = parseInt(argb[4], 16);
                      color.a = parseInt(argb[1], 16) / 255;
                      return tinycolor(
                        `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a})`
                      ).toHexString();
                    } catch (e) {
                      console.log(e);
                    }
                  })(cell.style.fill.fgColor.argb);
                }

                if (backGroundColor) {
                  cell.style.bgcolor = backGroundColor;
                }
                //*************************************************************************** */

                //*********************字体存在背景色******************************
                // 字体颜色
                let fontColor = null;
                if (
                  cell.style.font &&
                  cell.style.font.color &&
                  cell.style.font.color.argb
                ) {
                  // 8位字符颜色先转rgb再转16进制颜色
                  fontColor = ((val) => {
                    val = val.trim().toLowerCase(); //去掉前后空格
                    let color = {};
                    try {
                      let argb =
                        /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(
                          val
                        );
                      color.r = parseInt(argb[2], 16);
                      color.g = parseInt(argb[3], 16);
                      color.b = parseInt(argb[4], 16);
                      color.a = parseInt(argb[1], 16) / 255;
                      return tinycolor(
                        `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a})`
                      ).toHexString();
                    } catch (e) {
                      console.log(e);
                    }
                  })(cell.style.font.color.argb);
                }
                if (fontColor) {
                  cell.style.color = fontColor;
                }
                //************************************************************************ */

                // exceljs 对齐的格式转成 x-date-spreedsheet 能识别的对齐格式
                if (cell.style.alignment && cell.style.alignment.horizontal) {
                  cell.style.align = cell.style.alignment.horizontal;
                  cell.style.valign = cell.style.alignment.vertical;
                }
                cell.style.border = {};

                //处理合并单元格
                let mergeAddress = that.tools.lodash.find(
                  mergeAddressData,
                  function (o) {
                    return o.startAddress == cell._address;
                  }
                );
                if (mergeAddress) {
                  // 遍历的单元格属于合并单元格
                  if (cell.master.address != mergeAddress.startAddress) {
                    // 不是合并单元格中的第一个单元格不需要计入数据源
                    return;
                  }
                  sheetData.styles.push(cell.style);
                  // 说明是合并单元格区域的起始单元格
                  sheetData.rows[rowIndex].cells[colNumber] = {
                    text: cellText,
                    merge: [mergeAddress.YRange, mergeAddress.XRange],
                    style: sheetData.styles.length - 1, //对应的style存放序号
                  };
                } else {
                  // 非合并单元格
                  //解析单元格,包含样式
                  sheetData.styles.push(cell.style);
                  sheetData.rows[rowIndex].cells[colNumber] = {
                    text: cellText,
                    style: sheetData.styles.length - 1,
                  };
                }
              });
            });

            // 遍历列
            sheetData.cols = {};
            if (sheet.columns && sheet.columns.length) {
              sheet.columns = sheet.columns.slice(0, maxRow); // 不记录没有数据的列
              for (let i = 0; i < sheet.columns.length; i++) {
                sheetData.cols[i.toString()] = {};
                if (sheet.columns[i].width) {
                  // 不知道为什么从 exceljs 读取的宽度显示到 x-data-spreadsheet 特别小, 这里乘以8
                  sheetData.cols[i.toString()].width =
                    sheet.columns[i].width * 8;
                } else {
                  // 默认列宽
                  sheetData.cols[i.toString()].width = 100;
                }
              }
            }

            workbookData.push(sheetData);
          });
          this._templateData = workbookData;
          this.setTemplateData();
          this.$refs.upload.value = "";
        });
      };
    },
    // 导出excel
    exportExcel() {
      let new_wb = XLSX.utils.book_new();
      this.xs.getData().forEach(function (xws) {
        let aoa = [[]];
        let rowobj = xws.rows;
        for (let ri = 0; ri < rowobj.len; ++ri) {
          let row = rowobj[ri];
          if (!row) continue;
          aoa[ri] = [];
          Object.keys(row.cells).forEach(function (k) {
            let idx = +k;
            if (isNaN(idx)) return;
            let text = row.cells[k].text;
            aoa[ri][idx] = /^=/.test(text) ? { f: text } : text;
          });
        }
        let ws = XLSX.utils.aoa_to_sheet(aoa);
        XLSX.utils.book_append_sheet(new_wb, ws, xws.name || "sheet1");
      });
      XLSX.writeFile(new_wb, "模板.xlsx");
    },
    exportImg() {
      let el = document.querySelector("#spreadsheetEl");
      let outImg = el.toDataURL();
      let a = document.createElement("a");
      let event = new MouseEvent("click");

      a.download = "报表下载";
      a.href = outImg;
      a.dispatchEvent(event);
    },
    exportPdf() {
      let el = document.querySelector("#spreadsheetEl");
      this.downPdf(el);
      // this.exportImg()
      return;
    },
    async downPdf(html) {
      
      html2canvas(html).then((canvas) => {
      // 新建PDF文档
      console.log(canvas, "canvas");
      const pdf = new jsPDF();

      // 将canvas转换成图片
      const imgData = canvas.toDataURL("image/png");
      console.log(imgData, "imgData");
      // 添加图片到PDF
      const imgProps = pdf.getImageProperties(imgData);
      const pdfWidth = pdf.internal.pageSize.getWidth();
      const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width;
      console.log(pdfWidth, pdfHeight, "pdfWidth, pdfHeight");
      pdf.addImage(imgData, "PNG", 0, 0, pdfWidth, pdfHeight);

      // 保存生成的PDF
      pdf.save("spreadsheet.pdf");
      });
    },
    calFormula(data, content) {
      try {
        let fn = new Function("$", "return " + content);
        let res = fn(data);

        typeof res === "number" && (res = res.toFixed(2));

        if (/[\+\-*\/]/g.test(content) && isNaN(res)) {
          // 包含计算公式，做容错
          res = "";
        }

        return res;
      } catch (e) {
        return "";
      }
    },
  },
};
</script>

<style lang="less">
.c-spreadsheet {
  position: relative;
  padding: 0 10px;
  display: flex;
  flex-direction: column;
  // height: 100%;
  &.report {
    .x-spreadsheet {
      transform: translate(-39px, -35px);
    }
    .x-spreadsheet-selector-area {
      border: none;
    }
    .x-spreadsheet-selector-corner {
      display: none;
    }
    .x-spreadsheet-selector-area {
      background-color: transparent;
    }
  }
  .toolbar {
    padding-bottom: 16px;
    border-bottom: 1px solid #e0e2e4;
    .ivu-btn {
      margin-left: 16px;
    }
    &:empty {
      display: none;
    }
  }
  // &.noReport .spreadsheetWp {
  //     overflow: auto;
  // }
  .spreadsheetWp {
    position: relative;
    overflow: hidden;
    background: #fff;
  }
  .x-spreadsheet-selector-area {
    transform: translate(1px, 1px);
  }
  .x-spreadsheet-toolbar {
    padding: 0px;
    width: 100% !important;
  }
}
</style>
